package org.oneboot.core.utils;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.oneboot.core.annotation.IgnoreCompare;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * 对比两个对像的差异字段，用在记录日志场景
 * 
 * @author shiqiao.pro
 * @see https://boot.codeaone.com
 */
@Slf4j
public class CompareClassHelper {
	@Data
	public static class DifferField {
		/** 变更字段 */
		private String field;

		/** 当前取值 */
		private Object newValue;

		/** 变更前值 */
		private Object oldValue;
	}

	/**
	 * 获取比较的类中 忽略的字段
	 *
	 * @param objectClass
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static List<String> getCompareIgnoreFields(Class objectClass) {
		// 获取所有的字段
		Field[] fields = objectClass.getDeclaredFields();
		List<String> map = new ArrayList<>();
		for (Field f : fields) {
			// 判断字段注解是否存在
			boolean annotationPresent2 = f.isAnnotationPresent(IgnoreCompare.class);

			if (annotationPresent2) {
				IgnoreCompare ignoreCompare = f.getAnnotation(IgnoreCompare.class);
				// 获取注解值
				boolean ignore = ignoreCompare.ignore();
				if (ignore) {
					map.add(f.getName());
				}
			}
		}
		return map;
	}

	/**
	 * 比较两个实体属性值，返回一个map以有差异的属性名为key，value为一个Map分别存oldObject,newObject此属性名的值
	 *
	 * @param old
	 *            进行属性比较的对象1
	 * @param obj2
	 *            进行属性比较的对象2
	 * @return 属性差异比较结果map
	 */
	@SuppressWarnings("rawtypes")
	public static List<DifferField> compareFields(Object old, Object obj2, Class clazz) {
		List<DifferField> list = new ArrayList<>();
		try {
			List<String> ignoreList = getCompareIgnoreFields(clazz);

			// 只有两个对象都是同一类型的才有可比性
			if (old.getClass() == obj2.getClass()) {
				Class claz = old.getClass();
				// 获取object的属性描述
				PropertyDescriptor[] pds = Introspector.getBeanInfo(claz, Object.class).getPropertyDescriptors();
				// 这里就是所有的属性了
				for (PropertyDescriptor pd : pds) {
					// 属性名
					String name = pd.getName();
					// 如果当前属性选择忽略比较，跳到下一次循环
					if (ignoreList != null && ignoreList.contains(name)) {
						continue;
					}
					// get方法
					Method readMethod = pd.getReadMethod();
					// 在obj1上调用get方法等同于获得obj1的属性值
					Object o1 = readMethod.invoke(old);
					// 在obj2上调用get方法等同于获得obj2的属性值
					Object o2 = readMethod.invoke(obj2);
					if (o1 instanceof Timestamp) {
						o1 = new Date(((Timestamp) o1).getTime());
					}
					if (o2 instanceof Timestamp) {
						o2 = new Date(((Timestamp) o2).getTime());
					}
					if (o1 == null && o2 == null) {
						continue;
					} else if (o1 == null && o2 != null) {
						DifferField df = new DifferField();
						df.setField(name);
						df.setOldValue(o1);
						df.setNewValue(o2);
						list.add(df);
						continue;
					}
					// 比较这两个值是否相等,不等就可以放入map了
					if (!o1.equals(o2)) {
						DifferField df = new DifferField();
						df.setField(name);
						df.setOldValue(o1);
						df.setNewValue(o2);
						list.add(df);
					}
				}
			}
			return list;
		} catch (Exception e) {
			log.error("对比两个对像差异字段error", e);
			return list;
		}
	}
}
